package org.ysh.core.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;
import org.ysh.core.cache.Constants;
import org.ysh.core.utils.ShiroUtils;
import org.ysh.core.utils.StringUtils;
import org.ysh.domain.User;
import org.ysh.service.ResourceService;
import org.ysh.service.UserService;

import javax.annotation.Resource;
import java.util.List;

/**
 * 自定义域，Shiro 从从 Realm 获取安全数据（如用户、角色、权限），就是说 SecurityManager 要验证用户身份，
 * 那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法；
 * 也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作；可以把 Realm 看成 DataSource，即安全数据源。
 */
@Component
public class CustomerRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Resource
    private ResourceService resourceService;


    public CustomerRealm() {
        super();
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("MD5");
        matcher.setHashIterations(2);
        matcher.setStoredCredentialsHexEncoded(true);
        setCredentialsMatcher(matcher);
        setCachingEnabled(true);
        setAuthenticationCachingEnabled(true);
        setAuthenticationCacheName("authenticationCache");
        setAuthorizationCachingEnabled(true);
        setAuthorizationCacheName("authorizationCache");
    }

    static SimpleAuthorizationInfo EMPTY = new SimpleAuthorizationInfo();

    /**
     * 获取授权信息
     * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved.
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        if(StringUtils.isEmpty(username))
            return null;

        User user = userService.findByUsername(username);
        //如果当前用户是admin，返回所有权限
        if("admin".equals(user.getUsername())){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            List<org.ysh.domain.Resource> resources = resourceService.findByPage(new org.ysh.domain.Resource());
            resources.forEach(resource -> {
                if(StringUtils.isNotEmpty(resource.getPermission())){
                    simpleAuthorizationInfo.addStringPermission(resource.getPermission());
                }
            });
            return simpleAuthorizationInfo;
        }

        //获取用户关联的角色
        List<org.ysh.domain.Resource> resources = resourceService.findByUser(ShiroUtils.getCurrentUser().getId());
        if(resources.isEmpty()) {
             return EMPTY;
        }

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        resources.forEach(resource -> {
            if(StringUtils.isNotEmpty(resource.getPermission())){
                simpleAuthorizationInfo.addStringPermission(resource.getPermission());
            }
        });
        return simpleAuthorizationInfo;
    }

    /**
     * 获取身份验证信息
     * @param token the authentication token containing the user's principal and credentials.
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();

        if(StringUtils.isEmpty(username)){
            throw new UnknownAccountException("未知的账户信息!");
        }

        User user = userService.findByUsername(username);
        if( null == user){
            throw new UnknownAccountException("未知的账户信息!");
        }

        Subject subject = SecurityUtils.getSubject();
        subject.getSession().setAttribute(Constants.CURRENT_USER, user);

        //获取用户拥有权限的全宗列表，并设置第一个为当前全宗
        subject.getSession().setAttribute(Constants.CURRENT_FONDS, "4018");

        return new SimpleAuthenticationInfo(username, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
    }
}
